package com.cuixichang.minimalism.domain;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cuixichang.minimalism.acl.RelevanceDesign;
import com.cuixichang.minimalism.core.mybatis.cnstants.BaseFieldNames;
import com.cuixichang.minimalism.core.security.design.JWTAnalysisDesign;
import com.cuixichang.minimalism.core.security.userdetails.CredentialDetail;
import com.cuixichang.minimalism.mapper.IFormValueMapper;
import com.cuixichang.minimalism.service.*;
import com.cuixichang.minimalism.transfer.dto.*;
import com.cuixichang.minimalism.transfer.po.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ILoginCommandServiceImpl implements ILoginCommandService {
    @Autowired
    private ILoginService iLoginService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private IRoleCommandService roleCommandService;
    @Autowired
    private ILoginRoleService iLoginRoleService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JWTAnalysisDesign jwtAnalysisDesign;
    @Autowired
    private RelevanceDesign relevanceDesign;
    @Autowired
    private IFormValueCommandService formValueCommandService;
    @Autowired
    private IFormValueService formValueService;
    @Autowired
    private IFormValueMapper iFormValueMapper;
    @Override
    public ILoginDTO searchById(Long id,String moduleName, CredentialDetail credentialDetail) {
       Assert.notNull(id,"账号Id为空");
       List<Long> allowSearchLoginIds = Arrays.asList(id);
       if(!Objects.nonNull(credentialDetail)){
          allowSearchLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
       }
       if(!allowSearchLoginIds.contains(id)) return null;
       ILoginDTO result = iLoginService.selectById(id);
       if(StrUtil.isNotBlank(moduleName) && Objects.nonNull(credentialDetail)){
          result.setExpands(formValueCommandService.getFormValue(credentialDetail.getSystemCode(), moduleName, String.valueOf(id)));
       }
       return result;
    }
    @Override
    @Transactional
    public String rigister(ILoginDTO iLoginDTO, String systemCode) {
        String password = iLoginDTO.getLoginPassword();
        ILoginDTO iLogin = insert(iLoginDTO, null);
        String moduleName = iLoginDTO.getModuleName();
        Map<String, Object> expands = iLoginDTO.getExpands();
        if(StrUtil.isNotBlank(moduleName) && !CollectionUtils.isEmpty(expands)){
           Map<String, String> stringObjectMap = formValueCommandService.setFormValue
                   (systemCode, moduleName, String.valueOf(iLogin.getId()), expands, CredentialDetail.builder().loginId(iLogin.getId()).build());
            iLoginService.updateById(ILoginDTO.builder().id(iLogin.getId()).createLoginId(iLogin.getId()).signature(JSONUtil.toJsonStr(stringObjectMap)).build());
        }
        List<IRoleDTO> defaultRoles = roleCommandService.selectDefaultRole(systemCode);
        Assert.notEmpty(defaultRoles,"查询默认角色信息为空,请联系管理员");
        defaultRoles.forEach(iRole -> iLoginRoleService.insertHasOption(ILoginRoleDTO.builder().loginId(iLogin.getId())
                .roleId(iRole.getId()).createLoginId(iLogin.getId()).build(),(dto)->
                iLoginRoleService.selectOne(ILoginRoleDTO.builder().loginId(iLogin.getId())
                        .roleId(iRole.getId()).build())));
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(iLogin.getLoginName(), password);
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        return jwtAnalysisDesign.getToken(authenticate);
    }

    @Override
    public ILoginDTO insert(ILoginDTO iLoginDTO, CredentialDetail credentialDetail) {
        Assert.notNull(iLoginDTO,"参数为空,请检查");
        String loginPassword = iLoginDTO.getLoginPassword();
        Assert.notNull(loginPassword,"密码不能为空");
        String loginName = iLoginDTO.getLoginName();
        Assert.hasText(loginName,"用户名不能为空");
        ILoginDTO iLogin = iLoginService.selectOne(ILoginDTO.builder().loginName(loginName).build());
        Assert.isNull(iLogin,"用户名已存在");
        String encodePassword = passwordEncoder.encode(loginPassword);
        iLoginDTO.setLoginPassword(encodePassword);
        Assert.notNull(iLoginDTO,"参数为空,请检查");
        iLoginDTO.setCreateLoginId(Objects.nonNull(credentialDetail)? credentialDetail.getLoginId(): 0L);
        iLoginDTO.setGroupId(Objects.nonNull(credentialDetail)?credentialDetail.getGroupId(): UUID.randomUUID().toString());
        iLoginDTO.setSignature(StrUtil.DELIM_START.concat(StrUtil.DELIM_END));
        ILoginDTO insert = iLoginService.insert(iLoginDTO);
        String moduleName = iLoginDTO.getModuleName();
        Map<String, Object> expands = iLoginDTO.getExpands();
        if(StrUtil.isNotBlank(moduleName) && !CollectionUtils.isEmpty(expands)){
           Map<String, String> stringObjectMap = formValueCommandService.setFormValue
                   (credentialDetail.getSystemCode(), moduleName, String.valueOf(insert.getId()), expands, credentialDetail);
            iLoginService.updateById(ILoginDTO.builder().id(insert.getId()).createLoginId(insert.getId()).signature(JSONUtil.toJsonStr(stringObjectMap)).build());
        }
        return insert;
    }

    @Override
    public Page<ILoginDTO> roleHasBindingLogins(ILoginDTO loginDTO, Long pageNo, Long pageSize, Long roleId, CredentialDetail credentialDetail) {
        Assert.notNull(roleId,"角色id不能为空");
        Assert.notNull(pageNo,"页码不能为空");
        Assert.notNull(pageSize,"页大小不能为空");
        List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().roleId(roleId).build(), (iLoginRole, queryWrapper) -> {
            if(Objects.nonNull(credentialDetail)){
                List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
                beAuthLoginIds.remove(credentialDetail.getLoginId());
                queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds),"login_id",beAuthLoginIds);
            }
        });
        if(CollectionUtils.isEmpty(iLoginRoles)) return new Page<ILoginDTO>(pageNo,pageSize);
        List<Long> hasBindingLoginIds = iLoginRoles.stream().map(IL -> IL.getLoginId()).collect(Collectors.toList());
        return iLoginService.selectPage(loginDTO,pageSize,pageNo,(iLogin, queryWrapper) -> {
            queryWrapper.in("id",hasBindingLoginIds);
            String keyword = loginDTO.getKeyword();
            if(StrUtil.isNotBlank(keyword)){
                queryWrapper.and(warp->{
                    warp.like("login_name",keyword).or().like("signature",keyword);
                });
            }
        });
    }

    @Override
    public Page<ILoginDTO> roleUnBindingLogins(ILoginDTO loginDTO, Long pageNo, Long pageSize,Long roleId, CredentialDetail credentialDetail) {
        Assert.notNull(roleId,"角色id不能为空");
        Assert.notNull(pageNo,"页码不能为空");
        Assert.notNull(pageSize,"页大小不能为空");
        Page<ILoginDTO> result = new Page<>(pageNo,pageSize);
        return Optional.ofNullable(credentialDetail).map(credential -> {
            List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credential.getLoginId()).stream()
                    .filter(id->!credential.getLoginId().equals(id)).collect(Collectors.toList());
            if(CollectionUtils.isEmpty(beAuthLoginIds)) return new Page<ILoginDTO>(pageNo,pageSize);
            List<ILoginRoleDTO> iLoginRoles = iLoginRoleService.selectList(ILoginRoleDTO.builder().roleId(roleId).build(), (iLoginRole, queryWrapper) -> {
                if(Objects.nonNull(credential.getLoginId())){
                    queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds),"login_id",beAuthLoginIds);
                }
            });
            if(!CollectionUtils.isEmpty(iLoginRoles)){
                List<Long> filterLoginIds = iLoginRoles.stream().map(IL -> IL.getLoginId()).collect(Collectors.toList());
                beAuthLoginIds.removeAll(filterLoginIds);
            }
            if(CollectionUtils.isEmpty(beAuthLoginIds)) return new Page<ILoginDTO>(pageNo,pageSize);
            return iLoginService.selectPage(loginDTO,pageSize,pageNo,(iLogin, queryWrapper) -> {
                queryWrapper.in("id",beAuthLoginIds);
                String keyword = loginDTO.getKeyword();
                if(StrUtil.isNotBlank(keyword)){
                     queryWrapper.and(warp->{
                        warp.like("login_name",keyword).or().like("signature",keyword);
                     });
                 }
            });
        }).orElse(result);
    }

    @Override
    public boolean updateById(ILoginDTO loginDTO, CredentialDetail credentialDetail) {
        Assert.notNull(loginDTO,"参数为空,请检查");
        Long id = loginDTO.getId();
        Assert.notNull(id,"id不能为空");
        Boolean allowUpdate = Optional.ofNullable(credentialDetail).map(credential -> {
            Optional<Long> first = relevanceDesign.beAuthLoginIds(credential.getLoginId()).stream()
                    .filter(loginId -> loginId.equals(id)).findFirst();
            return first.isPresent();
        }).orElse(true);

        if(allowUpdate){
            loginDTO.setCreateLoginId(Objects.isNull(credentialDetail)?0L:credentialDetail.getLoginId());
        }else {
            return false;
        }
        loginDTO.setLoginPassword(null);
        String moduleName = loginDTO.getModuleName();
        Map<String, Object> expands = loginDTO.getExpands();
        if(StrUtil.isNotBlank(moduleName) && !CollectionUtils.isEmpty(expands)){
           Map<String, String> stringObjectMap = formValueCommandService.setFormValue
                   (credentialDetail.getSystemCode(), moduleName, String.valueOf(id), expands, credentialDetail);
            loginDTO.setSignature(JSONUtil.toJsonStr(stringObjectMap));
        }
        return iLoginService.updateById(loginDTO, (iLogin, updateWrapper)->{
           updateWrapper.set("logoff_time",iLogin.getLogoffTime());
           updateWrapper.set("re_auth_time",iLogin.getReAuthTime());
        }) == 1;
    }

    @Override
    public boolean deleteById(Long id) {
        formValueService.deleteByCondition(IFormValueDTO.builder().groupId(String.valueOf(id)).build());
        iLoginRoleService.deleteByCondition(ILoginRoleDTO.builder().loginId(id).build());
        return iLoginService.deleteById(id) == 1;
    }

    @Override
    public boolean deleteByIdHasPreCheck(Long id, CredentialDetail credentialDetail) {
        if(Objects.nonNull(credentialDetail)){
            List<Long> cascadeLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
            Assert.isTrue(cascadeLoginIds.contains(id),"请求权限不足,请检查");
        }
        return deleteById(id);
    }

    @Override
    public Page<ILoginDTO> selectPageByConditional(ILoginDTO loginDTO, Long pageNo, Long pageSize, CredentialDetail credentialDetail) {
        Assert.notNull(credentialDetail,"用户认证信息未发现,请重新登录");
        loginDTO.setGroupId(credentialDetail.getGroupId());
        List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(credentialDetail.getLoginId());
        Page<ILoginDTO> page = iLoginService.selectPage(loginDTO, pageSize, pageNo, (iSystem, queryWrapper) -> {
            queryWrapper.in(!CollectionUtils.isEmpty(beAuthLoginIds), BaseFieldNames.CREATE_LOGIN_ID, beAuthLoginIds);
            String keyword = loginDTO.getKeyword();
            if(StrUtil.isNotBlank(keyword)){
                queryWrapper.and(warp->{
                    warp.like("login_name",keyword).or().like("signature",keyword);
                });
            }
        });
        String moduleName = loginDTO.getModuleName();
         List<ILoginDTO> records = page.getRecords();
        if(StrUtil.isNotBlank(moduleName) && !CollectionUtils.isEmpty(records)){
            records.forEach(login -> {
                login.setExpands(formValueCommandService.getFormPreviewValue(credentialDetail.getSystemCode(),
                        moduleName, String.valueOf(login.getId())));
            });
        }
        return page;
    }

    @Override
    public boolean allowUseLoginName(String loginName, Long matchLoginId) {
        Assert.hasText(loginName,"匹配登录账户名称不能为空");
        ILoginDTO iLogin = iLoginService.selectOne(ILoginDTO.builder().loginName(loginName).build());
        return Optional.ofNullable(iLogin).map(login-> login.getId().equals(matchLoginId)).orElse(true);
    }

    @Override
    public boolean updateCurrentPassword(String oldPassword, String newPassword, CredentialDetail credentialDetail) {
        Assert.hasText(oldPassword,"旧密码不能为空");
        Assert.hasText(newPassword,"新密码不能为空");
        Assert.isTrue(!oldPassword.equals(newPassword),"新旧密码不能相同,请检查");
        if(Objects.nonNull(credentialDetail)){
            ILoginDTO result = iLoginService.selectById(credentialDetail.getLoginId());
            if(Objects.nonNull(result)){
                boolean matches = passwordEncoder.matches(oldPassword, result.getLoginPassword());
                if(matches){
                    result.setLoginPassword(passwordEncoder.encode(newPassword));
                    return iLoginService.updateById(result) == 1;
                }
            }
        }
        return false;
    }

    @Override
    public boolean updateSubPassword(Long loginId, String password, CredentialDetail credentialDetail) {
        Assert.notNull(loginId,"账号Id不能为空");
        Assert.hasText(password,"密码不能为空");
        Assert.notNull(credentialDetail,"用户认证信息未发现,请重新登录");
        Long currentLoginId = credentialDetail.getLoginId();
        Assert.isTrue(!currentLoginId.equals(loginId),"非法的执行请求,请检查");
        List<Long> beAuthLoginIds = relevanceDesign.beAuthLoginIds(currentLoginId);
        Assert.isTrue(beAuthLoginIds.contains(loginId),"当前用户无权限操作");
        ILoginDTO loginDTO = iLoginService.selectById(loginId);
        if(Objects.nonNull(loginDTO)){
            loginDTO.setLoginPassword(passwordEncoder.encode(password));
            loginDTO.setCreateLoginId(currentLoginId);
            return iLoginService.updateById(loginDTO) == 1;
        }
        return false;
    }
}
